package drds.configuration.db.xml_mapping.configuration;

import drds.configuration.db.xml_mapping.dynamic_tag.sql_template.SqlTemplate;
import drds.configuration.db.xml_mapping.dynamic_tag.sql_template.XmlMapperBuilder;
import drds.configuration.db.xml_mapping.dynamic_tag.xml_node.XmlNode;
import drds.configuration.db.xml_mapping.dynamic_tag.xml_node.XmlNodeParser;
import drds.configuration.db.xml_mapping.sql_execute.Mapper;
import drds.configuration.db.xml_mapping.sql_execute.SqlType;
import drds.configuration.db.xml_mapping.sql_execute.primary_key_generator.AutoSetModePrimaryKeyGenerator;
import drds.configuration.db.xml_mapping.sql_execute.primary_key_generator.NoPrimaryKeyGenerator;
import drds.configuration.db.xml_mapping.sql_execute.primary_key_generator.PrimaryKeyGenerator;
import drds.tools.$;
import drds.tools.Constants;
import drds.tools.Log;
import lombok.extern.slf4j.Slf4j;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;


@Slf4j
public class XmlMappersBuilder {
    private final String mapperXmlId;
    private final XmlNodeParser xmlNodeParser;
    private final ParameterMapAndResultMapAssistant parameterMapAndResultMapAssistant;
    public Configuration configuration;

    public XmlMappersBuilder(InputStream inputStream, Configuration configuration, String classFullClassName) {
        this(//
                new XmlNodeParser(inputStream, true, new Properties(), new XmlMappersEntityResolver()),//
                configuration, //
                classFullClassName);//
    }

    private XmlMappersBuilder(XmlNodeParser xmlNodeParser, Configuration configuration, String classFullClassName) {
        this.configuration = configuration;
        this.mapperXmlId = classFullClassName;//类名标记xml地址
        this.xmlNodeParser = xmlNodeParser;
        this.parameterMapAndResultMapAssistant = new ParameterMapAndResultMapAssistant(configuration, classFullClassName);

    }

    public void parse() {
        if (!configuration.containsMapperXmlId(mapperXmlId)) {
            configuration.addMapperXmlId(mapperXmlId);//先加入其他地方就不会重复进行处理
            xmlParse(xmlNodeParser.evaluateNode("/mapper"));
        }
    }

    /**
     * 对mapper.xml进行整体解析
     */
    private void xmlParse(XmlNode xmlNode) {
        try {
            //
            parameterMapsParse(xmlNode.evaluateXmlNodeList("/mapper/parameterMap"));
            resultMapsParse(xmlNode.evaluateXmlNodeList("/mapper/resultMap"));
            //
            mappersParse(xmlNode.evaluateXmlNodeList("select|insert|update|delete"));
        } catch (Exception e) {
            if (Constants.developMode) {
                log.error($.printStackTraceToString(e));
            }
            throw new RuntimeException("dynamic_tag xml解析失败:" + mapperXmlId + "," + e, e);
        }
    }


    private void parameterMapsParse(List<XmlNode> xmlNodeList) {
        for (XmlNode xmlNode : xmlNodeList) {
            String id = xmlNode.getStringAttribute("id");
            String parameterClassName = xmlNode.getStringAttribute("parameterClass");
            Class<?> parameterClass = null;
            try {
                parameterClass = Class.forName(parameterClassName);
            } catch (ClassNotFoundException e) {
                log.error($.printStackTraceToString(e));
                throw new RuntimeException("类找不到:" + parameterClassName);
            }
            List<XmlNode> parameterXmlNodeList = xmlNode.evaluateXmlNodeList("parameter");
            List<ParameterMapping> parameterMappingList = new ArrayList<>();
            for (XmlNode parameterXmlNode : parameterXmlNodeList) {
                String propertyName = parameterXmlNode.getStringAttribute("propertyName");
                String columnName = parameterXmlNode.getStringAttribute("columnName");
                ParameterMapping parameterMapping = parameterMapAndResultMapAssistant.buildParameterMapping(propertyName, columnName);
                parameterMappingList.add(parameterMapping);
            }
            parameterMapAndResultMapAssistant.addParameterMap(id, parameterClass, parameterMappingList);
        }
    }

    //
    private void resultMapsParse(List<XmlNode> xmlNodeList) throws Exception {
        for (XmlNode xmlNode : xmlNodeList) {
            resultMapParse(xmlNode);
        }
    }

    private ResultMap resultMapParse(XmlNode resultMapXmlNode) throws Exception {
        String resultClassName = resultMapXmlNode.getStringAttribute("resultClass");
        Class<?> resultClass = Class.forName(resultClassName);
        List<ResultMapping> resultMappingList = new ArrayList<>();
        List<XmlNode> childrenXmlNodeList = resultMapXmlNode.getChildrenXmlNodeList();
        for (XmlNode xmlNode : childrenXmlNodeList) {
            resultMappingList.add(buildResultMapping(xmlNode));
        }
        String id = resultMapXmlNode.getStringAttribute("id", resultMapXmlNode.getValueBasedIdentifier());
        //仅仅自己的mapper内可以使用
        return parameterMapAndResultMapAssistant.addResultMap(mapperXmlId + "#" + id, resultClass, resultMappingList);
    }

    private ResultMapping buildResultMapping(XmlNode xmlNode) throws Exception {
        String property = xmlNode.getStringAttribute("propertyName");
        String column = xmlNode.getStringAttribute("columnName");
        String columnPrefix = xmlNode.getStringAttribute("columnPrefix");
        return parameterMapAndResultMapAssistant.buildResultMapping(columnPrefix, property, column);
    }

    //
    private void mappersParse(List<XmlNode> xmlNodeList) {
        for (XmlNode xmlNode : xmlNodeList) {
            try {
                parseMapperNode(configuration, parameterMapAndResultMapAssistant, xmlNode, mapperXmlId);
            } catch (RuntimeException e) {
                Log.debugWithLine(log, Thread.currentThread(), $.printStackTraceToString(e));
                throw new RuntimeException(e);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void parseMapperNode(Configuration configuration, ParameterMapAndResultMapAssistant parameterMapAndResultMapAssistant, XmlNode mapperXmlNode, String mapperXmlId) throws Exception {
        //
        String id = mapperXmlNode.getStringAttribute("id");

        String parameterClassName = mapperXmlNode.getStringAttribute("parameterClass");
        Class<?> parameterClass = null;
        if ($.isNotNullAndNotEmpty(parameterClassName)) {
            parameterClass = Class.forName(parameterClassName);
        }
        String parameterMap = mapperXmlNode.getStringAttribute("parameterMap");
        if ($.isNotNullAndNotEmpty(parameterClassName)) {
            parameterMap = mapperXmlId + "#" + parameterMap;
        }
        //
        SqlType sqlType = SqlType.valueOf(mapperXmlNode.getNode().getNodeName().toLowerCase());
        PrimaryKeyGenerator primaryKeyGenerator = mapperXmlNode.getBooleanAttribute("usePrimaryKeyGenerator", SqlType.insert.equals(sqlType))
                ? AutoSetModePrimaryKeyGenerator.INSTANCE : NoPrimaryKeyGenerator.INSTANCE;
        //
        String primaryKeyAlias = mapperXmlNode.getStringAttribute("primaryKeyAlias");
        String primaryKeyPropertyName = mapperXmlNode.getStringAttribute("primaryKeyPropertyName");
        String primaryKeyColumnName = mapperXmlNode.getStringAttribute("primaryKeyColumnName");
        if (primaryKeyGenerator instanceof AutoSetModePrimaryKeyGenerator) {
            if ($.isNullOrEmpty(primaryKeyPropertyName) || $.isNullOrEmpty(primaryKeyColumnName)) {
                throw new RuntimeException("请配置主键策略相关参数");
            }
        }
        //

        //
        String resultClassName = mapperXmlNode.getStringAttribute("resultClass");
        Class<?> resultClass = null;
        if ($.isNotNullAndNotEmpty(resultClassName)) {
            resultClass = Class.forName(resultClassName);
        }
        String resultMap = mapperXmlNode.getStringAttribute("resultMap");
        if ($.isNotNullAndNotEmpty(resultMap)) {
            resultMap = mapperXmlId + "#" + resultMap;
        }
        //
        SqlTemplate sqlTemplate = createSql(configuration, mapperXmlNode);
        addMapper(
                parameterMapAndResultMapAssistant,//
                mapperXmlId + "#" + id,//
                sqlType, sqlTemplate,//
                parameterClass, parameterMap,//
                resultClass, resultMap,//
                primaryKeyGenerator,
                primaryKeyAlias,
                primaryKeyPropertyName,
                primaryKeyColumnName
        );
    }

    public SqlTemplate createSql(Configuration configuration, XmlNode mapperXmlNode) {
        XmlMapperBuilder xmlMapperBuilder = new XmlMapperBuilder(configuration, mapperXmlNode);
        return xmlMapperBuilder.parseMapperXmlNode();
    }

    public Mapper addMapper(//
                            ParameterMapAndResultMapAssistant parameterMapAndResultMapAssistant,//
                            String id,//
                            SqlType sqlType, SqlTemplate sqlTemplate,//
                            Class<?> parameterType, String parameterMapId,//
                            Class<?> resultType, String resultMapId,//
                            PrimaryKeyGenerator primaryKeyGenerator,//
                            String primaryKeyAlias, String primaryKeyPropertyName, String primaryKeyColumnName//
                            //SqlParser sqlParser
    ) {
        Mapper mapper = new Mapper();
        mapper.setConfiguration(configuration);
        mapper.setMapperXmlId(mapperXmlId);
        mapper.setId(id);
        mapper.setSqlType(sqlType);

        mapper.setParameterMap(parameterMapAndResultMapAssistant.getParameterMap(parameterMapId));
        mapper.setResultMap(parameterMapAndResultMapAssistant.getResultMap(resultMapId));

        mapper.setPrimaryKeyGenerator(primaryKeyGenerator);
        mapper.setPrimaryKeyAlias(primaryKeyAlias);
        mapper.setPrimaryKeyPropertyName(primaryKeyPropertyName);
        mapper.setPrimaryKeyColumnName(primaryKeyColumnName);
        mapper.setSqlTemplate(sqlTemplate);
        configuration.addMapper(mapper);
        if (Constants.developMode) {
            log.debug("addMapperInterfaceClass:" + id);
        }
        return mapper;
    }

}
